import fillin from "../../apis/fillin";
import {action, observable, toJS} from "mobx";
import {comsType, ruleTypes, viewTypes} from "../../constants/form";
import isMobile, {isWX, isWXWork} from "../../utils/mobileDetect";
import WX from "../../components/designer/form/view/third-party/wx";
import {propsComToViewCom} from "../../components/designer/form/component";
import {isAttachment, isNormal, isOptional, isPicture, isVisible, needValid} from "../../components/designer/form/component/typeof";
import {getTheme} from "../../components/designer/form/theme";

const { FIELD, JUMP } = ruleTypes;
const { PC, MOBILE, MOBILE_WX } = viewTypes;
const { FORM_HEADER, FORM_FOOTER } = comsType;
const defaultTheme = getTheme();

export default class FormViewStore {
  @observable shortkey = '';
  @observable formHeader = null;
  @observable formFooter = null;
  @observable components = null;
  @observable isInit = false;
  @observable errorCom = null;
  @observable submiting = false;
  @observable success = false;
  @observable error = null;
  @observable viewType = isMobile() ? MOBILE : PC;
  @observable formdata = null;
  @observable rightLevel = 0;
  @observable oauthKey = null;
  @observable oauthError = null;
  @observable theme = defaultTheme;

  @action init = shortkey => {
    if(!shortkey || this.shortkey) return;

    this.shortkey = shortkey;
    this.theme = getTheme('default');

    fillin.check(shortkey).then(res => {
      this.error = null;
      this.rightLevel = res.rightLevel;
      Promise.all([
        fillin.getPageInfo(shortkey),
        fillin.getRules(shortkey),
        this.thridPartyInit(),
      ]).then(([res, rules]) => {
        const coms = res.comps;
        if (res.style) {
          const style = JSON.parse(res.style);
          style && (this.theme = style);

          const background = style.background;
          if(background && background.type == 0){
            this.addbgStyle(background.css);
          }
        }
        const _rules = type => rules.filter(r => type === r.type);
        const components = coms.filter(isNormal).map(propsComToViewCom);

        this.error = null;
        this.isInit = true;
        this.rules = _rules(FIELD)
        this.formHeader = coms.find(com => com.type === FORM_HEADER);
        this.formFooter = coms.find(com => com.type === FORM_FOOTER);

        this.rules.forEach(({
          target
        }) => {
          target.split(',').forEach(tId => {
            const com = components.find(com => tId === com.uuid);

            com.visible = false;
          });
        });

        this.components = components;
        this.jumpRule = _rules(JUMP);

        // 处理默认值符合规则的情况
        if(this.rules.length) {
          this.components.filter(com => com.value)
            .forEach(com => this.triggerRules(com, com.value));
          this.components = toJS(this.components);
        }

        document.title = this.formHeader.config.title;

        if(this.oauthError) {
          this.toggleFooterDisable(true);
        }
      }).catch((res) => {
        this.error = res;
      });
    })
      .catch(error => {
        this.error = error;
      });
  }

  @action initOauthKey = code => {
    if (!code || this.oauthKey) return;

    this.oauthError = null;
    const oauthKey = localStorage.getItem(code);
    if (oauthKey) {
      this.oauthKey = oauthKey;
    } else {
      localStorage.clear();
      fillin.genOauthKey(code).then((res) => {
        if (res) {
          localStorage.setItem(code, res);
          this.oauthKey = res;
        } else {
          this.oauthError = 'error oauth';
        }
      }).catch(error => {
        this.oauthError = error || 'error';
      });
    }
  }

  addbgStyle(css) {

    let cssRule = ".wea-edc-fv-wrapper-mobilebg:before {content: '';position: fixed;" +
      "top: 0;" +
      "height: 100vh;" +
      "width: 100vw;" +
      "left: 0;" +
      "background: "+css.backgroundImage+" center 0 "+css.backgroundRepeat+";" +
      "background-size: "+css.backgroundSize+";}";
    let styleElement = document.getElementById('styles_mobilebg');
    if (!styleElement) {
      styleElement = document.createElement('style');
      styleElement.type = 'text/css';
      styleElement.id = 'styles_mobilebg';
      document.getElementsByTagName('head')[0].appendChild(styleElement);
    }
    styleElement.appendChild(document.createTextNode(cssRule));
  }


  //多条跳转规则存在时, 取最后一个组件的跳转路径
  getJumpUrl(coms) {
    const jumpRule = this.jumpRule.filter(rule => {
      let result = false;

      coms.forEach(com => {
        if(rule.trigger === com.uuid && com.value.length) {
          com.value.forEach(val => {
            if(rule.option === val.key) {
              result = true;
            }
          });
        }
      });
      return result;
    });

    const _index = uuid => this.components.findIndex(com => uuid === com.uuid);
    const rules = jumpRule.sort((r1, r2) => {
      const i1 = _index(r1.trigger);
      const i2 = _index(r2.trigger);

      return i1 < i2 ? 1 : -1;
    });

    if(rules.length) return rules[0].target;

    return '';
  }

  @action onValueChange = (uuid, value) => {
    const com = this.getComponent(uuid);

    if(com.value === value) return;

    com.value = value;

    this.triggerRules(com, value);
    this.clearError(com, this.getComponent(com.pid));
    this.components = toJS(this.components);
  }

  triggerRules(triggerCom, value) {
    if(!isOptional(triggerCom)) return;

    const allTargetComs = []; // 所有符合规则的target com
    const values = value.map(v => v.key);
    const rules = this.rules.filter(r => {
      return triggerCom.uuid === r.trigger;
    }).map(({ option, target }) => {
      const targets = target.split(',');
      const coms = targets.map(this.getComponent);

      allTargetComs.push(...coms);

      return { coms, option };
    });

    // 重置target的visible
    allTargetComs.forEach(com => com.visible = false);
    rules.forEach(({ option, coms }) => {
      coms.forEach(com => {
        // 防止多条规则作用于同一个字段时，字段显示值被多次覆盖
        // 如A,B规则都需要显示字段C，不return则会出现visible被覆盖的问题
        if(com.visible) return;

        com.visible = values.includes(option)
      });
    });
  }

  getComponent = uuid => {
    if(!uuid) return null;

    return this.components.find(com => uuid === com.uuid);
  }

  @action onSubmit = () => {
    if(this.submiting) return;

    const { valid, errorCom } = this.isFormValid();
    if(valid) {
      const coms = this.getComs();
      const _toogleLoading = isLoading => {
        this.formFooter.loading = isLoading;
        this.formFooter = toJS(this.formFooter);
        this.submiting = isLoading;
      };

      _toogleLoading(true);

      const jumpUrl = this.getJumpUrl(coms);
      const formdata = this.getFormData(coms);

        fillin.submit(formdata).then(() => {
        if(jumpUrl) {
          return window.open(jumpUrl, "_self");
        }
        this.submiting = false;
        this.success = true;
      }).catch(() => {
        _toogleLoading(false);
      });
    } else {
      this.components = toJS(this.components);
      this.errorCom = errorCom;
    }
  }

  @action toggleFooterDisable = (disable) => {
    this.formFooter.disable = disable;
    this.formFooter = toJS(this.formFooter);
    this.submiting = disable;
  }

  @action resetErrorCom = () => {
    this.errorCom = null;
  }

  thridPartyInit() {
    const iswx = isWX();
    const iswxWork = isWXWork();

    if(!iswx && !iswxWork) return;

    const url = location.href.split('#')[0];

    return fillin.getThirdPartyConfig(this.shortkey, url).then(({ wxConfig }) => {
      if(!wxConfig || !wxConfig.appId) return;

      return new WX(wxConfig).init().then(() => {
        this.viewType = !!window.wx ? MOBILE_WX : MOBILE;
      });
    });
  }

  isFormValid() {
    const coms = this.components.filter(needValid);
    let errorCom = coms.find(com => {
      return !this.isComValid(com);
    });

    if(errorCom && errorCom.pid) {
      const parentCom = this.getComponent(errorCom.pid);

      parentCom.errorCom = errorCom;
      errorCom = parentCom;
    }

    return {
      errorCom,
      valid: !errorCom,
    };
  }

  isComValid(com) {
    com.valid = true;
    com.error = '';

    com.rules.every(rule => {
      const { error, valid } = this.validate(rule, com);

      if(valid) return true;

      com.valid = false;
      com.error = error;
    });

    return com.valid;
  }

  validateComplxCom(complexCom, subCom) {
    const { config } = complexCom;
    const subComIds = config.complex.map(sub => config[sub].uuid);
    const ids = subCom ? subComIds.slice(subComIds.indexOf(subCom.uuid)) : subComIds;
    const coms = this.components.filter(({ uuid }) => ~ids.indexOf(uuid));

    complexCom.errorCom = coms.find(com => !this.isComValid(com));
  }

  validate(rule, field) {
    const valid = rule.validate(field);
    const error = !valid ? rule.error(field) : '';

    return { valid, error };
  }

  clearError(subCom, complexCom) {
    const hasNoError = !subCom.error || (complexCom && !complexCom.errorCom);

    if(hasNoError) return;

    const isvalid = this.isComValid(subCom);

    if(isvalid && complexCom) {
      this.validateComplxCom(complexCom);
    }
  }

  getFormData(coms) {
    const {oauthKey} = this;
    oauthKey && this.formdata.append('oauthKey', oauthKey);
    this.formdata.append('key', this.shortkey);
    this.formdata.append('datas', JSON.stringify(coms));

    return this.formdata;
  }

  getComs() {
    this.formdata = new FormData();

    return this.components
      .filter(isVisible)
      .map(com => {
        let { uuid, value } = com;
        if((isAttachment(com) || isPicture(com)) && value) {
          let docIds = [];
          value.forEach(({ docid }) => {
            docid > 0 && (docIds.push(docid));
          });
          value = docIds.join(',');
        }

        return { uuid, value };
      });
  }
}